#include <cstdint>
#include <iostream>
#include <syscap_ndk.h>
//
// Created on 2025/10/31.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#include "../include/ScreenRecorder.h"
#include "aki/jsbind.h"

#include <multimedia/player_framework/native_avscreen_capture.h>
#include <multimedia/player_framework/native_avscreen_capture_base.h>
#include <multimedia/player_framework/native_avbuffer.h>
#include <multimedia/player_framework/native_avscreen_capture_errors.h>
#include "hilog/log.h"
#include <string>
#include "multimedia/player_framework/native_avcodec_videodecoder.h"
#include "multimedia/player_framework/native_avcodec_videoencoder.h"
#include <native_buffer/native_buffer.h>
#include "native_window/external_window.h"
#include <multimedia/player_framework/native_avcapability.h>
#include <multimedia/player_framework/native_avcodec_base.h>
#include <multimedia/player_framework/native_avbuffer.h>
#include <napi/native_api.h>
#include<nlohmann/json.hpp>
using json = nlohmann::json;


namespace parse_json {
    OH_AudioCaptureInfo parseAudioCaptureInfo(const json &data){
        OH_AudioCaptureInfo ans={};
        if (data.contains("audioSampleRate") && data["audioSampleRate"].is_number_integer()) {
            ans.audioSampleRate=data["audioSampleRate"].get<int32_t>();
        }
        if (data.contains("audioChannels") && data["audioChannels"].is_number_integer()) {
            ans.audioChannels=data["audioChannels"].get<int32_t>();
        }
        if (data.contains("audioSource") && data["audioSource"].is_number_integer()) {
            ans.audioSource=data["audioSource"].get<OH_AudioCaptureSourceType>();
        }
        return ans;
    }

    OH_AudioEncInfo parseAudioEncInfo(const json& data){
        OH_AudioEncInfo ans={};
        if (data.contains("audioBitrate") && data["audioBitrate"].is_number_integer()) {
            ans.audioBitrate=data["audioBitrate"].get<int32_t>();
        }
        if (data.contains("audioCodecformat") && data["audioCodecformat"].is_number_integer()) {
            ans.audioCodecformat=data["audioCodecformat"].get<OH_AudioCodecFormat>();
        }
        return ans;
    }

    OH_AudioInfo parseAudioInfo(const json &data){
        OH_AudioInfo ans={};
        
        const auto& audioInfoJson = data;
        if (audioInfoJson.contains("micCapInfo") && audioInfoJson["micCapInfo"].is_object()) {
            const auto& micCapInfoJson = data["micCapInfo"];
            ans.micCapInfo=parseAudioCaptureInfo(micCapInfoJson);
        }
        if (audioInfoJson.contains("innerCapInfo") && audioInfoJson["innerCapInfo"].is_object()) {
            const auto& innerCapInfoJson = data["innerCapInfo"];
            ans.innerCapInfo=parseAudioCaptureInfo(innerCapInfoJson);
        }
        if (audioInfoJson.contains("audioEncInfo") && audioInfoJson["audioEncInfo"].is_object()) {
            const auto& audioEncInfoJson = data["audioEncInfo"];
            ans.audioEncInfo=parseAudioEncInfo(audioEncInfoJson);
        }
        
    
        return ans;
    }
    
    OH_VideoCaptureInfo parseVideoCaptureInfo(const json &data){
        OH_VideoCaptureInfo ans={};
        if (data.contains("displayId") && data["displayId"].is_number_integer()) {
            ans.displayId=data["displayId"].get<int32_t>();
        }
        if (data.contains("videoFrameWidth") && data["videoFrameWidth"].is_number_integer()) {
            ans.videoFrameWidth=data["videoFrameWidth"].get<int32_t>();
        }
        if (data.contains("videoFrameHeight") && data["videoFrameHeight"].is_number_integer()) {
            ans.videoFrameHeight=data["videoFrameHeight"].get<int32_t>();
        }
        if (data.contains("videoSource") && data["videoSource"].is_number_integer()) {
            ans.videoSource=data["videoSource"].get<OH_VideoSourceType>();
        }
        return ans;
    }
    
    OH_VideoEncInfo parseVideoEncInfo(const json &data){
        OH_VideoEncInfo ans={};
        if (data.contains("videoCodec") && data["videoCodec"].is_number_integer()) {
            ans.videoCodec=data["videoCodec"].get<OH_VideoCodecFormat>();
        }
        
        if (data.contains("videoBitrate") && data["videoBitrate"].is_number_integer()) {
            ans.videoBitrate=data["videoBitrate"].get<int32_t>();
        }
        if (data.contains("videoFrameRate") && data["videoFrameRate"].is_number_integer()) {
            ans.videoFrameRate=data["videoFrameRate"].get<int32_t>();
        }
        return ans;
    }

    OH_VideoInfo parseVideoInfo(const json& data){
        OH_VideoInfo ans={};
        if (data.contains("videoCapInfo") && data["videoCapInfo"].is_object()) {
            const auto& videoCapInfoJson = data["videoCapInfo"];
            if (videoCapInfoJson.contains("videoCapInfo") && videoCapInfoJson["videoCapInfo"].is_object()) {
                const auto& videoCapInfoJson = data["videoCapInfo"];
                ans.videoCapInfo=parseVideoCaptureInfo(videoCapInfoJson);
            }
        
        }
        if (data.contains("videoEncInfo") && data["videoEncInfo"].is_object()) {
            const auto& videoEncInfoJson = data["videoEncInfo"];
            if (videoEncInfoJson.contains("videoEncInfo") && videoEncInfoJson["videoEncInfo"].is_object()) {
                const auto& videoEncInfoJson = data["videoEncInfo"];
                ans.videoEncInfo=parseVideoEncInfo(videoEncInfoJson);
            }
        
        }
        return ans;
    }

    OH_RecorderInfo parseRecorderInfo(const json& data){
        OH_RecorderInfo ans={};
        if (data.contains("urlLen") && data["urlLen"].is_number_integer()) {
            ans.urlLen=data["urlLen"].get<uint32_t>();
        }
    
        if (ans.urlLen && data.contains("url") && data["url"].is_string()) {
            char* ptr = new char[ans.urlLen + 1];
            std::string s=data["url"].get<std::string>();
            std::strcpy(ptr, s.c_str());
            ans.url=ptr;
        }
        if (data.contains("fileFormat") && data["fileFormat"].is_number_integer()) {
            ans.fileFormat=data["fileFormat"].get<OH_ContainerFormatType>();
        }
    
        return ans;
    }
};
bool ScreenRecorder::isRunning=false;

ScreenRecorder& ScreenRecorder::instance(){
    static ScreenRecorder instance;
    return instance;
}



OH_AVScreenCaptureConfig  SetConfig(json j){
    OH_AVScreenCaptureConfig config={};
    if (j.contains("audioInfo") && j["audioInfo"].is_object()) {
        OH_AudioInfo audioInfo=parse_json::parseAudioInfo(j["audioInfo"]);
        config.audioInfo=audioInfo;
    }
    if (j.contains("videoInfo") && j["videoInfo"].is_object()) {
        OH_VideoInfo videoInfo=parse_json::parseVideoInfo(j["videoInfo"]);
        config.videoInfo=videoInfo;
    }
    
    /* should be set, while dataType = OH_CAPTURE_FILE */
    if (j.contains("recorderInfo") && j["recorderInfo"].is_object()) {
        OH_RecorderInfo recorderInfo=parse_json::parseRecorderInfo(j["recorderInfo"]);
        config.recorderInfo=recorderInfo;
    }
    
    auto captureMode=j.value(json::json_pointer("/captureMode"), OH_CaptureMode::OH_CAPTURE_HOME_SCREEN);
    config.captureMode=captureMode;
    auto dataType=j.value(json::json_pointer("/captureMode"), OH_DataType::OH_ORIGINAL_STREAM);
    config.dataType=dataType;
    
    return config;
}

void ScreenRecorder::OnBufferAvailable(OH_AVScreenCapture *capture, OH_AVBuffer *buffer, OH_AVScreenCaptureBufferType bufferType,
                          int64_t timestamp, void *userData)
   {
       if (isRunning) {
           OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture OnBufferAvailable bufferType is %{public}d",
               bufferType);
           if (bufferType == OH_SCREEN_CAPTURE_BUFFERTYPE_VIDEO) {
               // 处理视频buffer
               OH_NativeBuffer *nativebuffer = OH_AVBuffer_GetNativeBuffer(buffer);
               if (nativebuffer != nullptr) {
                   int bufferLen = OH_AVBuffer_GetCapacity(buffer);
                   OH_AVCodecBufferAttr info;
                   int32_t ret = OH_AVBuffer_GetBufferAttr(buffer, &info);
                   OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture size %{public}d", info.size);
                   OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture bufferLen %{public}d", bufferLen);

                   OH_NativeBuffer_Config config;
                   OH_NativeBuffer_GetConfig(nativebuffer, &config);
                   OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture height %{public}d width %{public}d",
                       config.height, config.width);
                   // int32_t length = config.height * config.width * 4;
                   uint8_t *buf = OH_AVBuffer_GetAddr(buffer);
                   if (buf == nullptr) {
                       return;
                   }
                   auto jsFunc = aki::JSBind::GetJSFunction("m2b_oh_BufferTypeVideoCallback");
                   if(jsFunc){
                        aki::ArrayBuffer tsData(buf,bufferLen);
                       // 当在非 JS 线程使用 ArrayBuffer 时，如果数据字节流的内存生命周期在 ArrayBuffer 使用前结束，则需要暂存。
                       // 此处不确定。实测注释后，速度有提升 
                       // tsData.Commit();
                       // Invoke 指定 JS 方法的返回值类型
                       jsFunc->Invoke<void>(tsData); // 可在非JS线程执行
                   }else{
                        OH_LOG_WARN(LOG_APP, "m2b_oh_BufferTypeVideoCallback is not found!");  
                    }
                
                   OH_NativeBuffer_Unreference(nativebuffer);
                   buffer = nullptr;
               }
           } else if (bufferType == OH_SCREEN_CAPTURE_BUFFERTYPE_AUDIO_INNER) {
               // 处理内录buffer
               int bufferLen = OH_AVBuffer_GetCapacity(buffer);
               uint8_t *buf = OH_AVBuffer_GetAddr(buffer);
               if (buf != nullptr) {
                   OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture OnBufferAvailable inner audio");
                   auto jsFunc = aki::JSBind::GetJSFunction("m2b_oh_AudioInnerCallback");
                   if(jsFunc){
                        aki::ArrayBuffer tsData(buf,bufferLen);
                       // 当在非 JS 线程使用 ArrayBuffer 时，如果数据字节流的内存生命周期在 ArrayBuffer 使用前结束，则需要暂存。
                       // 此处不确定。实测注释后，速度有提升 
                       // tsData.Commit();
                       // Invoke 指定 JS 方法的返回值类型
                       jsFunc->Invoke<void>(tsData); // 可在非JS线程执行
                   }
                    else{
                        OH_LOG_WARN(LOG_APP, "m2b_oh_AudioInnerCallback is not found!");
                    }
                   
               }
           } else if (bufferType == OH_SCREEN_CAPTURE_BUFFERTYPE_AUDIO_MIC) {
               // 处理麦克风buffer
               int bufferLen = OH_AVBuffer_GetCapacity(buffer);
               uint8_t *buf = OH_AVBuffer_GetAddr(buffer);
               if (buf != nullptr) {
                   OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture OnBufferAvailable mic audio");
                   auto jsFunc = aki::JSBind::GetJSFunction("m2b_oh_AudioMicCallback");
                   if(jsFunc){
                        aki::ArrayBuffer tsData(buf,bufferLen);
                       // 当在非 JS 线程使用 ArrayBuffer 时，如果数据字节流的内存生命周期在 ArrayBuffer 使用前结束，则需要暂存。
                       // 此处不确定。实测注释后，速度有提升 
                       // tsData.Commit();
                       // Invoke 指定 JS 方法的返回值类型
                       jsFunc->Invoke<void>(tsData); // 可在非JS线程执行
                   }else{
                        OH_LOG_WARN(LOG_APP, "m2b_oh_AudioMicCallback is not found!");
                    }
                
               }
           }
       }
       return;
   }


void ScreenRecorder::OnDisplaySelected(struct OH_AVScreenCapture *capture, uint64_t displayId, void *userData){
    (void)capture;
    OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture OnError errorCode is %{public}uld", displayId);
    (void)userData;
}

void ScreenRecorder::OnError(OH_AVScreenCapture *capture, int32_t errorCode, void *userData){
    (void)capture;
    OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture OnError errorCode is %{public}d", errorCode);
    (void)userData;
}

void ScreenRecorder::OnStateChange(struct OH_AVScreenCapture *capture, OH_AVScreenCaptureStateCode stateCode, void *userData)
    {
//        if (stateCode == OH_SCREEN_CAPTURE_STATE_STARTED) {
//            OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture OnStateChange started");
//            // 处理状态变更
//            // 可选 配置录屏旋转
//            int32_t ret = OH_AVScreenCapture_SetCanvasRotation(capture, true);
//            // 可选 修改Canvas分辨率
//            ret = OH_AVScreenCapture_ResizeCanvas(g_avCapture, 768, 1280);
//            // 可选 设置是否显示光标
//            ret = OH_AVScreenCapture_ShowCursor(g_avCapture, true);
//            // 可选 设置视频最大帧率
//            ret = OH_AVScreenCapture_SetMaxVideoFrameRate(g_avCapture, 30);
//        }
//        if (stateCode == OH_SCREEN_CAPTURE_STATE_INTERRUPTED_BY_OTHER) {
//            // 处理状态变更
//        }
//        (void)userData;
    }

int ScreenRecorder::startScreenCapture(std::string& configStr,bool isMicrophone){
    json j;
    try {
        j = json::parse(configStr);
    // 处理 j
    } catch (const json::parse_error& e) {
        OH_LOG_ERROR(LOG_APP, "JSON parse error: %s",e.what());
        return -1;
        
    }
    if (!canIUse("SystemCapability.Multimedia.Media.AVScreenCapture")) {
        return -1;
    }
    // 释放之前分配的内存
    if (config_.recorderInfo.url != nullptr) {
        delete[] config_.recorderInfo.url;
        config_.recorderInfo.url = nullptr;
    }
    
    config_=SetConfig(j);
    g_avCapture = OH_AVScreenCapture_Create();
    if (g_avCapture == nullptr) {
        OH_LOG_ERROR(LOG_APP, "create screen capture failed");
    }
    
    
    if (!canIUse("SystemCapability.Multimedia.Media.AVScreenCapture")) {
        return 1;
    }
    OH_AVScreenCapture_SetMicrophoneEnabled(g_avCapture, isMicrophone);
    OH_AVScreenCapture_SetErrorCallback(g_avCapture, ScreenRecorder::OnError, nullptr);
    OH_AVScreenCapture_SetStateCallback(g_avCapture, ScreenRecorder::OnStateChange, nullptr);
    OH_AVScreenCapture_SetDataCallback(g_avCapture, ScreenRecorder::OnBufferAvailable, nullptr);
    OH_AVScreenCapture_SetDisplayCallback(g_avCapture, ScreenRecorder::OnDisplaySelected, nullptr);
    
    // 可选，排除指定窗口/指定音频类型 start
//    struct OH_AVScreenCapture_ContentFilter *contentFilter = OH_AVScreenCapture_CreateContentFilter();
//    // 添加过滤通知音。
//    OH_AVScreenCapture_ContentFilter_AddAudioContent(contentFilter, OH_SCREEN_CAPTURE_NOTIFICATION_AUDIO);
//    vector<int> windowIdsExclude = { -111 };
//    OH_AVScreenCapture_ContentFilter_AddWindowContent(contentFilter,
//        &windowIdsExclude[0], static_cast<int32_t>(windowIdsExclude.size()));
//    OH_AVScreenCapture_ExcludeContent(g_avCapture, contentFilter);
//    OH_AVScreenCapture_SkipPrivacyMode(g_avCapture,
//        &windowIdsExclude[0], static_cast<int32_t>(windowIdsExclude.size()));
//    OH_AVScreenCapture_ReleaseContentFilter(contentFilter);
    // 可选，排除指定窗口/指定音频类型 end
    int result = OH_AVScreenCapture_Init(g_avCapture, config_);
    if (result != AV_SCREEN_CAPTURE_ERR_OK) {
        OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture OH_AVScreenCapture_Init failed %{public}d", result);
    }
    OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture OH_AVScreenCapture_Init %{public}d", result);

    result = OH_AVScreenCapture_StartScreenCapture(g_avCapture);
    if (result != AV_SCREEN_CAPTURE_ERR_OK) {
        OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture Started failed %{public}d", result);
        OH_AVScreenCapture_Release(g_avCapture);
    }
    OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture Started %{public}d", result);

    isRunning = true;


    return result;
}

int ScreenRecorder::stopScreenCapture(){
    OH_LOG_INFO(LOG_APP, "==ScreenCaptureSample== ScreenCapture Stop");
    OH_AVSCREEN_CAPTURE_ErrCode result = AV_SCREEN_CAPTURE_ERR_OPERATE_NOT_PERMIT;
    
    if (g_avCapture == nullptr) {
        OH_LOG_ERROR(LOG_APP, "capture_ is null.");
    }
    
    result = OH_AVScreenCapture_StopScreenCapture(g_avCapture);
    if (result != AV_SCREEN_CAPTURE_ERR_BASE) {
        OH_LOG_ERROR(LOG_APP, "StopScreenCapture OH_AVScreenCapture_StopScreenCapture Result: %{public}d", result);
    }
    result = OH_AVScreenCapture_Release(g_avCapture);
    if (result != AV_SCREEN_CAPTURE_ERR_BASE) {
        OH_LOG_ERROR(LOG_APP, "StopScreenCapture OH_AVScreenCapture_Release: %{public}d", result);
    }
    OH_LOG_INFO(LOG_APP, "OH_AVScreenCapture_Release success");
    isRunning = false;
    
    return result;
}

ScreenRecorder::~ScreenRecorder() {
    // 释放config_中分配的内存
    if (config_.recorderInfo.url != nullptr) {
        delete[] config_.recorderInfo.url;
        config_.recorderInfo.url = nullptr;
    }
}